#ifndef __MP_IMPL__
#define __MP_IMPL__

#include <set>
#include <algorithm>
#include "mpfilter.h"
#include "mpmatcher.h"

namespace xm
{
	typedef std::vector<mp_p2r_t>	vec_p2r_t;

	class XManEngine;

	class mp_matcher : public mp_base_filter
	{
	public:

		typedef mp_base_filter super;

	protected:

		vec_p2r_t	_ptn_2_rec;
		bit_map_t	_hit_bmap;
		lgc_ptns_t	_lgc_ptns;

	public:

		virtual VOID clear()
		{
			super::clear();
			clear_p2r_map();
			clear_hit_map();
		}

		inline bit_map_t& hit_map()
		{
			return _hit_bmap;
		}
		inline vec_p2r_t& p2r_map()
		{
			return _ptn_2_rec;
		}
		inline VOID clear_p2r_map()
		{
			p2r_map().clear();
		}
		inline VOID clear_hit_map()
		{
			hit_map().resize(0);
		}
		inline VOID zero_hit_map()
		{
			hit_map().zero();
		}

		virtual HRESULT build_index( IKMDRTable * lpTable, cblob_t& br_data )
		{
			clear_p2r_map();
			clear_hit_map();

			RFAILED( lpTable->GetTableData( br_data ) );

			byte_region_ex_t bindat( br_data.Data, br_data.Length );

			size_t	nptnt = 0;
			while( bindat.rest_bytes() )
			{
				recp_t * rechp = 0;
				RASSERT( bindat.fetchp(rechp,1), E_FAIL );

				size_t nfmt = rechp->nfmt + 1;

				le::U16 * fmts = 0;
				RASSERT( bindat.fetchp(fmts, nfmt), E_FAIL );

				char *	name = 0;
				RASSERT( bindat.fetchp(name, rechp->naml), E_FAIL );

				uint8_t * kill = 0;
				RASSERT( bindat.fetchp(kill, rechp->killbc), E_FAIL );

				size_t fltnum = 0;
				size_t nptn = rechp->ptnnum;
				for( size_t i = 0; i < nptn; ++ i )
				{
					ptnp_t * ptnhp = 0;
					RASSERT( bindat.fetchp(ptnhp, 1), E_FAIL );

					LPCSTR ptndp = 0;
					RASSERT( bindat.fetchp(ptndp, ptnhp->bytes ), E_FAIL );

					if( ptnhp->opts & mp::PoptLogic )
						continue;
					
					++ fltnum;
				}

				nptnt += nfmt * fltnum;
			}

			enum { MIN_BITS = 4*1024 };

			size_t nbits = nptnt * 64 / 8;
			if( nbits && nbits < MIN_BITS ) nbits = MIN_BITS;

			RFAILED( super::bloom_filter().resize(nbits,true) );

			bindat.reset();

			while( bindat.rest_bytes() )
			{
				recp_t * rechp = 0;
				RASSERT( bindat.fetchp(rechp,1), E_FAIL );

				size_t nfmt = rechp->nfmt + 1;

				le::U16 * fmts = 0;
				RASSERT( bindat.fetchp(fmts, nfmt), E_FAIL );
				for( size_t i = 0; i < nfmt; ++ i )
					super::care_format( fmts[i] );

				char * name = 0;
				RASSERT( bindat.fetchp(name, rechp->naml), E_FAIL );

				uint8_t * kill = 0;
				RASSERT( bindat.fetchp(kill, rechp->killbc), E_FAIL );

				size_t nptn = rechp->ptnnum;
				for( size_t i = 0; i < nptn; ++ i )
				{
					ptnp_t * ptnhp = 0;
					RASSERT( bindat.fetchp(ptnhp, 1), E_FAIL );

					LPCSTR ptndp = 0;
					RASSERT( bindat.fetchp(ptndp, ptnhp->bytes ), E_FAIL );

					if( ptnhp->opts & mp::PoptLogic )
						continue;

					super::shift_table().add_binstr( ptndp );

					uint32_t lowmask = LOW_MASK;
					if( ptnhp->opts & mp::PoptCaseSense ) lowmask = 0;

					for( size_t f = 0; f < nfmt; ++ f )
						bloom_filter().add_binstr( ptndp, fmts[f], lowmask );

					mp_p2r_t p2r;
					p2r.hash6b = murmur32_low(ptndp,PREFIX_LENGTH,0);
					p2r.ptndp = ptndp;
					p2r.rhptr = rechp;
					p2r_map().push_back( p2r );
				}
			}

			size_t ptnall = p2r_map().size() + _lgc_ptns.size();

			std::sort( p2r_map().begin(), p2r_map().end() );
			if( !hit_map().resize( ptnall, true ) )
				return E_FAIL;

			return S_OK;
		}

		size_t logic_pttern_index( size_t idx )
		{
			return p2r_map().size() + idx;
		}

		static inline bool is_equl( vec_p2r_t::iterator& it, mp_p2r_t& fnd )
		{
			return it->hash6b == fnd.hash6b;
		}

		// call once
		inline recp_t * match_logic_records( xman_session_t * session, LPCSTR datp, size_t datcb )
		{
			size_t lc = _lgc_ptns.size();
			for( size_t i = 0; i < lc; ++ i )
			{
				p2r_t & p2r = _lgc_ptns[i];

				ptnp_t * ptnhp = (ptnp_t*)(p2r.ptndp) - 1;
				recp_t * rhp = p2r.rhptr;

				byte_region_ex_t br_data( datp, datp+datcb, datp );
				byte_region_t br_ptn( p2r.ptndp, ptnhp->bytes );

				session->match_ctrl = matcher_t::PM_CASE_SENS;

				matcher_t _matcher( session, br_ptn, br_data, 0 );

				byte_region_t matched;
				bool_t if_matched = _matcher.do_match( matched, true );
				if( !if_matched ) continue;

				hit_map().setbit( logic_pttern_index(i), true );

				int32_t  score = ptnhp->score;
				if( !score ) continue;

				rhp = session->handle_pattern_hit( p2r );
				if( rhp ) return rhp;
			}
			return NULL;
		}

		inline recp_t * match_records( xman_session_t * session, LPCSTR datstartp, LPCSTR datcurp, size_t datrest )
		{
			mp_p2r_t item;
			item.hash6b = murmur32_low( datcurp, PREFIX_LENGTH, 0 );
			item.ptndp = 0;
			item.rhptr = 0;

			vec_p2r_t::iterator _first = p2r_map().begin();
			vec_p2r_t::iterator _last = p2r_map().end();

			vec_p2r_t::iterator _hit = std::lower_bound( _first, _last, item );

			if( _hit == _last ) return NULL;
			if( !is_equl(_hit,item) ) return NULL;

			for(;;) 
			{
				if( !is_equl(_hit,item) ) { ++ _hit;  break; }
				if( _hit == _first ) break;
				-- _hit;
			};

			for( ; _hit < _last && is_equl( _hit, item ); ++ _hit )
			{
				size_t idx = _hit - _first;
				if( hit_map().testbit(idx) ) continue;

				recp_t * rhp = _hit->rhptr;

				le::PU16 fmtsp = fmtp(rhp);
				le::PU16 fmtspe = fmtsp + rhp->nfmt + 1;

				le::U16 now_fmt( (uint16_t)session->now_fmt );
				if( !std::binary_search( fmtsp, fmtspe, now_fmt ) )
					continue;

				ptnp_t * ptnhp = (ptnp_t*)(_hit->ptndp) - 1;

				byte_region_ex_t br_data( datstartp, datcurp+datrest, datcurp );
				byte_region_t br_ptn( _hit->ptndp, ptnhp->bytes );

				session->match_ctrl = (ptnhp->opts & mp::PoptCaseSense) ? matcher_t::PM_CASE_SENS : 0;

				matcher_t _matcher( session, br_ptn, br_data, 0 );

				byte_region_t matched;
				bool_t if_matched = _matcher.do_match( matched, true );
				if( !if_matched ) continue;

				if( !(ptnhp->opts & mp::PoptOverlapped) )
					hit_map().setbit(idx,true);

				uint32_t sigid = rhp->sigid;
				int32_t  score = ptnhp->score;
				if( !score ) continue;

				rhp = session->handle_pattern_hit( *_hit );
				if( !rhp ) continue; 

				return rhp;
			}
			return NULL;
		}
	};

};


#endif